package org.truenewx.tnxjee.webmvc.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import org.springframework.context.ApplicationContext;
import org.truenewx.tnxjee.core.Strings;
import org.truenewx.tnxjee.core.enums.EnumDictResolver;
import org.truenewx.tnxjee.core.enums.annotation.EnumProperty;
import org.truenewx.tnxjee.core.enums.annotation.SimpleEnumType;
import org.truenewx.tnxjee.core.util.BeanPropertyMeta;
import org.truenewx.tnxjee.core.util.LogUtil;
import org.truenewx.tnxjee.core.util.SpringUtil;
import org.truenewx.tnxjee.model.annotation.ComponentType;
import org.truenewx.tnxjee.model.validation.constraint.RegionCode;
import org.truenewx.tnxjee.service.spec.region.RegionSource;
import org.truenewx.tnxjee.webmvc.http.annotation.ResultEnumFilter;
import org.truenewx.tnxjee.webmvc.util.AttachFieldUtil;
import org.truenewx.tnxjee.webmvc.util.SpringWebMvcUtil;

import java.io.IOException;
import java.io.Serial;
import java.util.*;

/**
 * 附加字段的Bean序列化修改器
 */
public class AttachFieldBeanSerializerModifier extends BeanSerializerModifier {

    private final EnumDictResolver enumDictResolver;
    private final RegionSource regionSource;
    private final Map<Class<?>, ResultEnumFilter[]> resultEnumFiltersMapping = new HashMap<>();

    public AttachFieldBeanSerializerModifier(ApplicationContext context) {
        this.enumDictResolver = context.getBean(EnumDictResolver.class); // 一定有
        this.regionSource = SpringUtil.getFirstBeanByClass(context, RegionSource.class); // 可能没有
    }

    public void addResultEnumFilters(Class<?> beanClass, ResultEnumFilter[] resultEnumFilters) {
        this.resultEnumFiltersMapping.put(beanClass, resultEnumFilters);
    }

    private ResultEnumFilter getResultEnumFilter(Object bean, String propertyName) {
        if (bean != null) {
            ResultEnumFilter[] resultEnumFilters = this.resultEnumFiltersMapping.get(bean.getClass());
            if (resultEnumFilters != null) {
                for (ResultEnumFilter resultEnumFilter : resultEnumFilters) {
                    if (propertyName.equals(resultEnumFilter.name())) {
                        return resultEnumFilter;
                    }
                }
            }
        }
        return null;
    }

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
            List<BeanPropertyWriter> beanProperties) {
        for (int i = 0; i < beanProperties.size(); i++) {
            BeanPropertyWriter writer = beanProperties.get(i);
            BeanPropertyMeta meta = getPropertyMeta(writer);
            if (meta != null) {
                beanProperties.set(i, new BeanPropertyWriter(writer) {

                    @Serial
                    private static final long serialVersionUID = 6267157125639776096L;

                    @Override
                    public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov)
                            throws Exception {
                        super.serializeAsField(bean, gen, prov);

                        String propertyName = meta.getName();
                        ResultEnumFilter resultEnumFilter = getResultEnumFilter(bean, propertyName);
                        if (resultEnumFilter == null || !resultEnumFilter.pure()) {
                            Object rawValue = getMember().getValue(bean);
                            if (rawValue != null) {
                                String subType = resultEnumFilter == null ? Strings.EMPTY : resultEnumFilter.subType();
                                Locale locale = SpringWebMvcUtil.getLocale();
                                Object caption = AttachFieldUtil.getAttachedCaptionValue(meta, rawValue,
                                        AttachFieldBeanSerializerModifier.this.enumDictResolver,
                                        AttachFieldBeanSerializerModifier.this.regionSource, subType, locale);
                                if (caption != null) {
                                    String captionFieldName = AttachFieldUtil.getAttachedCaptionFieldName(propertyName);
                                    if (caption instanceof String) {
                                        gen.writeStringField(captionFieldName, (String) caption);
                                    } else {
                                        gen.writeObjectField(captionFieldName, caption);
                                    }
                                }
                                // 附加除显示名称外的其它附加字段
                                AttachFieldUtil.loopAttachedProperty(meta, rawValue,
                                        AttachFieldBeanSerializerModifier.this.enumDictResolver, locale,
                                        (attachedFieldName, attachedFieldValue) -> {
                                            try {
                                                if (attachedFieldValue instanceof String) {
                                                    gen.writeStringField(attachedFieldName,
                                                            (String) attachedFieldValue);
                                                } else {
                                                    gen.writeObjectField(attachedFieldName, attachedFieldValue);
                                                }
                                            } catch (IOException e) {
                                                LogUtil.error(getClass(), e);
                                            }
                                        });
                            }
                        }
                    }

                });
            }
        }
        return beanProperties;
    }

    private BeanPropertyMeta getPropertyMeta(BeanPropertyWriter writer) {
        JavaType propertyType = writer.getType();
        Class<?> rawClass = propertyType.getRawClass();
        boolean multi = false;
        if (rawClass.isArray()) {
            rawClass = rawClass.getComponentType();
            multi = true;
        } else if (Collection.class.isAssignableFrom(rawClass)) {
            ComponentType componentType = writer.getAnnotation(ComponentType.class);
            if (componentType == null) { // 集合属性没有配置@ComponentType注解，无法获知元素类型，则忽略
                return null;
            }
            rawClass = componentType.value();
            multi = true;
        }
        if (rawClass.isEnum() || rawClass.getAnnotation(SimpleEnumType.class) != null) {
            return new BeanPropertyMeta(writer.getName(), multi, rawClass);
        } else {
            EnumProperty enumProperty = writer.getAnnotation(EnumProperty.class);
            if (enumProperty != null) {
                return new BeanPropertyMeta(writer.getName(), multi, enumProperty);
            }
            if (this.regionSource != null) {
                RegionCode regionCode = writer.getAnnotation(RegionCode.class);
                if (regionCode != null) {
                    return new BeanPropertyMeta(writer.getName(), multi, regionCode);
                }
            }
        }
        return null;
    }

}
